package org.unidata.mdm.rest.v1.data.service.favorite;

import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.HttpMethod;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.parameters.RequestBody;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.unidata.mdm.core.util.SecurityUtils;
import org.unidata.mdm.data.service.FavoriteEtalonsService;
import org.unidata.mdm.rest.system.ro.DetailedErrorResponseRO;
import org.unidata.mdm.rest.system.ro.ErrorResponse;
import org.unidata.mdm.rest.v1.data.ro.favorite.AddFavoriteRequestRO;
import org.unidata.mdm.rest.v1.data.ro.favorite.AddFavoriteResultRO;
import org.unidata.mdm.rest.v1.data.ro.favorite.DeleteFavoriteRequestRO;
import org.unidata.mdm.rest.v1.data.ro.favorite.DeleteFavoriteResultRO;
import org.unidata.mdm.rest.v1.data.ro.favorite.GetFavoriteRequestRO;
import org.unidata.mdm.rest.v1.data.ro.favorite.GetFavoriteResultRO;
import org.unidata.mdm.rest.v1.data.service.AbstractDataRestService;
import org.unidata.mdm.system.type.runtime.MeasurementContextName;
import org.unidata.mdm.system.type.runtime.MeasurementPoint;

/**
 * Service for working with favorites
 *
 * @author Alexandr Serov
 * @since 19.10.2020
 **/
@Path("favorite-etalons")
@Consumes({MediaType.APPLICATION_JSON})
@Produces({MediaType.APPLICATION_JSON})
public class FavoriteEtalonsRestService extends AbstractDataRestService {

    private static final String FAVORITE_TAG = "favorite";
    private static final String UUID_PATH_PARAM = "uuid";
    private static final String ENTITY_NAME_PATH_PARAM = "entityName";

    @Autowired
    private FavoriteEtalonsService favoriteEtalonsService;

    @GET
    @Operation(
        description = "Get user favorite etalons.",
        method = HttpMethod.GET,
        responses = {
            @ApiResponse(content = @Content(schema = @Schema(implementation = DetailedErrorResponseRO.class)), responseCode = "400"),
            @ApiResponse(content = @Content(schema = @Schema(implementation = DetailedErrorResponseRO.class)), responseCode = "500"),
            @ApiResponse(content = @Content(schema = @Schema(implementation = GetFavoriteResultRO.class)), responseCode = "200")
        }, tags = FAVORITE_TAG
    )
    public GetFavoriteResultRO etalons(@Parameter(in = ParameterIn.QUERY, description = "Entity name") @QueryParam("entityName") String entityName) {
        GetFavoriteRequestRO req = new GetFavoriteRequestRO();
        req.setEntityName(entityName);
        return executeGetFavorite(req);
    }

    @GET
    @Path("/is-favorite/{" + UUID_PATH_PARAM + "}")
    @Operation(description = "Check is favorite etalon.", responses = {
        @ApiResponse(content = @Content(schema = @Schema(implementation = DetailedErrorResponseRO.class)), responseCode = "400"),
        @ApiResponse(content = @Content(schema = @Schema(implementation = DetailedErrorResponseRO.class)), responseCode = "500"),
    }, tags = FAVORITE_TAG)
    public Boolean isFavorite(@Parameter(in = ParameterIn.PATH, description = "Etalon ID (UUID)") @PathParam(UUID_PATH_PARAM) UUID uuid) {
        return uuid != null && favoriteEtalonsService.isFavorite(uuid);
    }

    /**
     * Add to favorites.
     *
     * @param request to save
     * @return created record
     */
    @POST
    @Operation(
        description = "Add to favorite.",
        method = HttpMethod.POST,
        requestBody = @RequestBody(content = @Content(schema = @Schema(implementation = AddFavoriteRequestRO.class)), description = "Add to favorite request"),
        responses = {
            @ApiResponse(content = @Content(schema = @Schema(implementation = AddFavoriteResultRO.class)), responseCode = "200"),
            @ApiResponse(content = @Content(schema = @Schema(implementation = DetailedErrorResponseRO.class)), responseCode = "400"),
            @ApiResponse(content = @Content(schema = @Schema(implementation = DetailedErrorResponseRO.class)), responseCode = "500")
        }, tags = FAVORITE_TAG
    )
    public AddFavoriteResultRO addToFavorite(AddFavoriteRequestRO request) {
        return executeAddFavorite(request);
    }

    @DELETE
    @Path("/{" + UUID_PATH_PARAM + "}")
    @Operation(
        description = "Remove from favorite.",
        method = HttpMethod.DELETE,
        responses = {
            @ApiResponse(content = @Content(schema = @Schema(implementation = DeleteFavoriteResultRO.class)), responseCode = "200"),
            @ApiResponse(content = @Content(schema = @Schema(implementation = ErrorResponse.class)), responseCode = "500")
        }, tags = FAVORITE_TAG
    )
    public DeleteFavoriteResultRO deleteByUUID(@Parameter(in = ParameterIn.PATH, description = "ID записи (UUID).") @PathParam(UUID_PATH_PARAM) UUID uuid) {
        DeleteFavoriteRequestRO req = new DeleteFavoriteRequestRO();
        req.setIds(Collections.singletonList(uuid));
        return executeDeleteFavorite(req);
    }

    /**
     *
     */
    @DELETE
    @Path("/entity/{" + ENTITY_NAME_PATH_PARAM + "}")
    @Operation(
        description = "Remove from favorite.",
        method = HttpMethod.DELETE,
        responses = {
            @ApiResponse(content = @Content(schema = @Schema(implementation = DeleteFavoriteResultRO.class)), responseCode = "200"),
            @ApiResponse(content = @Content(schema = @Schema(implementation = DetailedErrorResponseRO.class)), responseCode = "400"),
            @ApiResponse(content = @Content(schema = @Schema(implementation = DetailedErrorResponseRO.class)), responseCode = "500")
        }, tags = FAVORITE_TAG
    )
    public DeleteFavoriteResultRO deleteByEntityName(@Parameter(in = ParameterIn.PATH, description = "Имя реестра / справочника.") @PathParam(ENTITY_NAME_PATH_PARAM) String entityName) {
        DeleteFavoriteRequestRO req = new DeleteFavoriteRequestRO();
        req.setEntityName(entityName);
        return executeDeleteFavorite(req);
    }

    private GetFavoriteResultRO executeGetFavorite(GetFavoriteRequestRO req) {
        Objects.requireNonNull(req, "Get request can't be null");
        GetFavoriteResultRO result = new GetFavoriteResultRO();
        if (StringUtils.isNotBlank(req.getEntityName())) {
            result.setEtalons(favoriteEtalonsService.getEtalons(req.getEntityName()));
        } else {
            result.setEtalons(favoriteEtalonsService.getEtalons());
        }
        return result;
    }

    private AddFavoriteResultRO executeAddFavorite(AddFavoriteRequestRO req) {
        Objects.requireNonNull(req, "Add favorite request can't be null");
        MeasurementPoint.init(MeasurementContextName.MEASURE_UI_CREATE);
        MeasurementPoint.start();
        try {
            AddFavoriteResultRO result = new AddFavoriteResultRO();
            if (MapUtils.isNotEmpty(req.getFavorites())) {
                favoriteEtalonsService.add(req.getFavorites());
                result.setSuccess(true);
            }
            return result;
        } finally {
            MeasurementPoint.stop();
        }
    }

    private DeleteFavoriteResultRO executeDeleteFavorite(DeleteFavoriteRequestRO req) {
        Objects.requireNonNull(req, "Delete request can't be null");
        List<UUID> uuids = req.getIds();
        String entityName = req.getEntityName();
        DeleteFavoriteResultRO result = new DeleteFavoriteResultRO();
        if (uuids != null) {
            favoriteEtalonsService.exclude(uuids);
            result.setDeleted(true);
        } else if (StringUtils.isNotBlank(entityName)) {
            favoriteEtalonsService.excludeByEntity(entityName, SecurityUtils.getCurrentUserName());
            result.setDeleted(true);
        }
        return result;
    }

    public FavoriteEtalonsService getFavoriteEtalonsService() {
        return favoriteEtalonsService;
    }

    public void setFavoriteEtalonsService(FavoriteEtalonsService favoriteEtalonsService) {
        this.favoriteEtalonsService = favoriteEtalonsService;
    }
}